#!/usr/bin/python3
# -*- coding: utf-8; tab-width: 4; indent-tabs-mode: t -*-

import os
import re
import sys
import time
import json
import manpa
import socket
import shutil
import jinja2
import pathlib
import subprocess
import mirrors.plugin
from selenium.webdriver.common.by import By


class Main:

    def __init__(self, sock):
        self.sock = sock
        self.dataDir = mirrors.plugin.params["storage-file"]["data-directory"]
        self.downloadDir = os.path.join(self.dataDir, "__download__")

        self.p = InfoPrinter()
        self.failFlag = False

        self.drvList = [
            {
                "driver-name": "RealTek RTL8139(A/B/C/D/8130)/810X",
                "driver-version": "",
                "hardware": [
                    "RealTek/RTL8139A",
                    "RealTek/RTL8139B",
                    "RealTek/RTL8139C",
                    "RealTek/RTL8139D",
                    "RealTek/RTL8130",
                    "RealTek/RTL810X",
                ]
                "url": "https://www.driverguide.com/driver/detail.php?driverid=759568&si=0f24a7e290db581bbff180f739a8e97e",
            },
        ]



    def run(self):
        # FIXME: use this width/height so that "use cookie" dialog won't get in the way, it should be eliminated by manpa
        with manpa.Manpa(width=2500, height=2000) as mp:
            mp.set_warning_callback(self._warnCallback)
            with mp.open_selenium_client() as driver:
                startUrl = 'https://dynamicwallpaper.club/gallery?section=new'
                while startUrl is not None:
                    # get remote url list
                    wallpaperUrlDict = dict()
                    self.p.print("Reading wallpapers...")
                    self.p.incIndent()
                    try:
                        driver.open_page(startUrl)
                        i = 0
                        while True:
                            self.p.print("Page %d: %s" % (i + 1, driver.current_url))
                            self.p.incIndent()
                            try:
                                for aTag in driver.find_elements(by=By.XPATH, value=".//div[@class='gallery-item']//div[@class='wallpaper-info']//a"):
                                    wallpaperUrl = aTag.get_attribute("href")
                                    if os.path.basename(os.path.dirname(wallpaperUrl)) == "wallpaper" and os.path.basename(wallpaperUrl) not in localInfoDict:
                                        self.p.print("Wallpaper recorded: %s, %s" % (aTag.text, wallpaperUrl))
                                        wallpaperUrlDict[wallpaperUrl] = aTag.text                                  # wallpaper name may duplicate, so use url as key
                            finally:
                                self.p.decIndent()

                            nextPageTagList = driver.find_elements(by=By.XPATH, value='.//a[@class="link bright featured"]')
                            if len(nextPageTagList) == 0:
                                startUrl = None
                                break

                            assert len(nextPageTagList) == 1
                            startUrl = nextPageTagList[0].get_attribute("href")

                            # too many wallpapers, we download them in several batches
                            if len(wallpaperUrlDict) > 10:
                                break

                            driver.open_page(nextPageTagList[0].get_attribute("href"))
                            i += 1
                    finally:
                        self.p.decIndent()

                    # get wallpaper
                    self.p.print("Downloading wallpapers...")
                    self.p.incIndent()
                    try:
                        for url, name in wallpaperUrlDict.items():
                            self.p.print("Downloading wallpaper \"%s\" from \"%s\"..." % (name, url))

                            driver.open_page(url)

                            # FIXME: manpa should eliminate this dialog
                            buttonTagList = driver.find_elements(by=By.XPATH, value=".//button[@aria-label='Do not consent']")
                            if len(buttonTagList) > 0:
                                assert len(buttonTagList) == 1
                                driver.click_and_wait(buttonTagList[0])

                            # parse wallpaper tags
                            tags = []
                            for aTag in driver.find_elements(by=By.XPATH, value=".//a"):
                                if aTag.get_attribute("href") is not None:
                                    m = re.fullmatch(".*&tag=(.*)$", aTag.get_attribute("href"))
                                    if m is not None:
                                        tags.append(m.group(1))

                            # get wallpaper link
                            aTagList = driver.find_elements(by=By.XPATH, value=".//a[contains(@class,'download')]")
                            if len(aTagList) != 1:
                                self._failCallback("Strange that len(aTagList) is %d, not 1" % (len(aTagList)))
                                continue

                            # download wallpaper
                            fullfn = driver.click_for_downloaded_file(aTagList[0])
                            info = DriverInfo(os.path.basename(url), tags, os.path.basename(fullfn))

                            # create wallpaper directory
                            saveDirFullfn = os.path.join(self.downloadDir, info.id)
                            os.mkdir(saveDirFullfn)
                            shutil.move(fullfn, os.path.join(saveDirFullfn, info.fn))
                            info.save_to_file(os.path.join(saveDirFullfn, "info.json"))

                            # record info
                            localInfoDict[info.id] = info
                    finally:
                        self.p.decIndent()

        self.p.print("All Done.")


class DriverInfo:

    @staticmethod
    def create_info_file(driver_dir, driver_name, driver_type, driver_version, driver_release_date, hardware_ids, url):
        data = {
            "driver_name": driver_name,
            "driver_type": driver_type,
            "driver_version": driver_version,
            "driver_release_date": driver_release_date,
            "hardware_ids": hardware_ids,                       # format: vendor/hardware
            "url": url,
        }
        infoFullfn = os.path.join(driver_dir, "info.json")
        with open(infoFullfn, "w") as f:
            json.dump(data, f)

    def __init__(self, driver_dir):
        infoFullfn = os.path.join(driver_dir, "info.json")
        data = json.loads(pathlib.Path(infoFullfn).read_text())
        if len(data) != 3:
            raise Exception("bad, %s" % (driver_dir))

        self.driver_name = data["driver_name"]
        self.driver_type = data["driver_type"]
        self.driver_version = data["driver_version"]
        self.driver_release_date = data["driver_release_date"]
        self.driver_vendor_id = data["driver_vendor_id"]
        self.driver_vendor_name = data["driver_vendor_name"]
        self.hardware_ids = data["hardware_ids"]
        self.url = data["url"]
        self.main_dir = driver_dir
        self.files_dir = os.path.join(self.main_dir, "files")


class Util:

    @staticmethod
    def isRealFile(path):
        return os.path.isfile(path) and not os.path.islink(path)

    @staticmethod
    def isRealDir(path):
        return os.path.isdir(path) and not os.path.islink(path)

    @staticmethod
    def forceDelete(path):
        if os.path.islink(path):
            os.remove(path)
        elif os.path.isfile(path):
            os.remove(path)
        elif os.path.isdir(path):
            shutil.rmtree(path)
        elif os.path.lexists(path):
            os.remove(path)             # other type of file, such as device node
        else:
            pass                        # path does not exist, do nothing

    @staticmethod
    def getFreeSocketPort(portType):
        if portType == "tcp":
            stlist = [socket.SOCK_STREAM]
        elif portType == "udp":
            stlist = [socket.SOCK_DGRAM]
        elif portType == "tcp+udp":
            stlist = [socket.SOCK_STREAM, socket.SOCK_DGRAM]
        else:
            assert False

        for port in range(10000, 65536):
            bFound = True
            for sType in stlist:
                s = socket.socket(socket.AF_INET, sType)
                try:
                    s.bind((('', port)))
                except socket.error:
                    bFound = False
                finally:
                    s.close()
            if bFound:
                return port

        raise Exception("no valid port")


class InfoPrinter:

    def __init__(self):
        self.indent = 0

    def incIndent(self):
        self.indent = self.indent + 1

    def decIndent(self):
        assert self.indent > 0
        self.indent = self.indent - 1

    def print(self, s):
        line = ""
        line += "\t" * self.indent
        line += s
        print(line)


###############################################################################

if __name__ == "__main__":
    with mirrors.plugin.ApiClient() as sock:
        try:
            Main(sock).run()
            sock.progress_changed(100)
        except Exception:
            sock.error_occured(sys.exc_info())
            raise
